Linux电源管理

您所在的位置:网站首页 wake awake区别 Linux电源管理

Linux电源管理

2023-11-28 09:27| 来源: 网络整理| 查看: 265

文章基于 www.wowotech.net  的学习内容

总体的框架

wakeup events framework主要包括 wake lock, wakeup count, autosleep等机制

系统在suspend过程中的时候,当wakeup事件产生的时候,不能进入suspend状态

wakeup event framework就是解决用户空间和内核空间的同步问题的,包含下面情况

1. 驱动处理过程中,不允许进入suspend

2. 后续需要处理的用户进程,不会获取到wakeup events

3. 正在后续处理的用户进程,处理过程中,系统不能进入suspend

总体框架如下:

 wakeup events framework core就是linux关于wakeup event的核心框架,主要向驱动提供唤醒源注册,使能等接口。向上层提供上报,停止等接口,还有关于PM core的状态查询接口

sysfs文件

wake lock/unlock 提供给用户层面,可以阻止系统进入suspend的一个接口

wakeup count,用户上层用户查询wakeup event的一个接口

auto sleep就是设定系统没活动时,自动休眠的接口

 

关于wakeup source和wakeup event

1. 只有具有唤醒功能的设备才能作为wakeup source,具备唤醒功能的设备会被标识为唤醒能力,通过设备结构里面的can_wakeup标志标识,并且会在sysfs目录下有关于wakeup信息的文件存在

2. 具备唤醒功能的设备主要和 dev_pm_info结构有关

3. 一个wakeup source的设备,主要虚拟为结构体struct wakeup_source结构

1 struct wakeup_source { 2 const char *name; // 设备名字 3 struct list_head entry; 4 spinlock_t lock; 5 struct timer_list timer; 6 unsigned long timer_expires; 7 ktime_t total_time; 8 ktime_t max_time; 9 ktime_t last_time; 10 ktime_t start_prevent_time; 11 ktime_t prevent_sleep_time; 12 unsigned long event_count; //设备产生wakeup event的个数 13 unsigned long active_count; //产生wakeup event时,设备切换到active状态,这个 //表示了wakeup source设备的繁忙程度 14 unsigned long relax_count; 15 unsigned long expire_count; 16 unsigned long wakeup_count; //中断进入suspend状态的次数 17 bool active:1; 18 bool autosleep_enabled:1; 19 }; View Code

关于wakeup event framework核心功能

1. __pm_stay_awake : wakeup source 切换为active状态的接口

2. __pm_relax: wakeup source 切换为disactive状态的接口

3. __pm_wakeup_event: 上边两个接口的结合体,引入了时间控制

对于驱动设备常用的接口:

1 extern int device_wakeup_enable(struct device *dev); //使能wakeup功能 2 dev->power.should_wakeup = true; 3 extern int device_wakeup_disable(struct device *dev); 4 extern void device_set_wakeup_capable(struct device *dev, bool capable); 5 dev->power.can_wakeup = capable; //配置是否具备唤醒功能 6 extern int device_init_wakeup(struct device *dev, bool val);//初始化wakeup功能 7 device_set_wakeup_capable(dev, val); 8 device_set_wakeup_enable(dev, val); 9 extern int device_set_wakeup_enable(struct device *dev, bool enable); 10 dev->power.should_wakeup = enable; 11 extern void pm_stay_awake(struct device *dev); 12 __pm_stay_awake(dev->power.wakeup);// 调用系统接口操作struct wakeup source变量,处理wakeup events 13 extern void pm_relax(struct device *dev); 14 extern void pm_wakeup_event(struct device *dev, unsigned int msec); View Code

 

wakeup count

主要用于解决system suspend 和system wakeup events之间的同步问题

 wakeup count给上层提供了sysfs接口,给auto sleep提供了接口

实现的原理

1. 发生电源切换的实体先读取系统的wakeup count变量,并且告知wakeup events framework。

2. framework core保存这个变量到saved_count中

3. suspend过程中,有可能会发生wakeup events,所以某些时间点,会调用接口(pm_wakeup_pending),检查是否有wakeup需要处理

4. 如果有,代表读出来wakeup count 和saved_count不一样,这时需要终止suspend的过程

 

当调用类似 read(&cnt, "/sys/power/wakeup_count"); 的时候,系统最终会调用pm_get_wakeup_count

调用 write(cnt, "/sys/power/wakeup_count")的时候,系统最终会调用 pm_save_wakeup_count

pm_get_wakeup_count的主要实现:

1 bool pm_get_wakeup_count(unsigned int *count, bool block) 2 { 3 unsigned int cnt, inpr; 4 5 if (block) { 6 DEFINE_WAIT(wait); // 定义等待队列 7 8 for (;;) { 9 prepare_to_wait(&wakeup_count_wait_queue, &wait, 10 TASK_INTERRUPTIBLE);// 把wait加入等待队列链表里面,更改程序状态,一旦后面和wakeup_count_wait_queue相关的线程调用waitqueue_active就会遍历里面所有的wait,之后尝唤醒。 11 split_counters(&cnt, &inpr); 12 if (inpr == 0 || signal_pending(current)) //唤醒之后,等待inpr == 0 13 break; 14 15 schedule(); //条件不满足,继续睡眠 16 } 17 finish_wait(&wakeup_count_wait_queue, &wait); // 移除wait 18 } 19 20 split_counters(&cnt, &inpr); 21 *count = cnt; 22 return !inpr; 23 } View Code

pm_save_wakeup_count的主要实现

1 bool pm_save_wakeup_count(unsigned int count) 2 { 3 unsigned int cnt, inpr; 4 unsigned long flags; 5 6 events_check_enabled = false; //这个变量为false代表wakeup count功能不使用 7 spin_lock_irqsave(&events_lock, flags); 8 split_counters(&cnt, &inpr); 9 if (cnt == count && inpr == 0) { //满足所有disactive 10 saved_count = count; //保留count到saved_count 中 11 events_check_enabled = true; 12 } 13 spin_unlock_irqrestore(&events_lock, flags); 14 return events_check_enabled; 15 } View Code

前面的suspend过程中,最后阶段会调用suspend_enter函数:

1 static int suspend_enter(suspend_state_t state, bool *wakeup) 2 { 3 int error; 4 5 ... 6 7 error = syscore_suspend(); 8 if (!error) { 9 *wakeup = pm_wakeup_pending(); //check wakeup events,false代表放心睡 10 if (!(suspend_test(TEST_CORE) || *wakeup)) { 11 error = suspend_ops->enter(state); // 如果没有wakeup events事件,那么进行suspend状态切换 12 events_check_enabled = false; 13 } 14 syscore_resume(); //否则中断suspend过程 15 } 16 ... 17 return error; 18 } View Code

里面调用的pm_wakeup_pending,主要是:

1 bool pm_wakeup_pending(void) 2 { 3 unsigned long flags; 4 bool ret = false; 5 6 spin_lock_irqsave(&events_lock, flags); 7 if (events_check_enabled) { 8 unsigned int cnt, inpr; 9 10 split_counters(&cnt, &inpr); //读wakeup count和in progress count 11 ret = (cnt != saved_count || inpr > 0);如果不等,代表有wakeup event产生 12 events_check_enabled = !ret; 13 } 14 spin_unlock_irqrestore(&events_lock, flags); 15 16 if (ret) 17 print_active_wakeup_sources(); 18 19 return ret; 20 } View Code

以上就是wakeup在用户层和suspend过程中的使用方式

 

wake_lock/wake_unlock

sysfs下的 /sys/power/wake_lock & /sys/power/wake_unlock

总体的框架

 

 代码分析

wakeup_lock/wakeup_unlock的接口主要是下面的四个函数

1 static ssize_t wake_lock_show(struct kobject *kobj, 2 struct kobj_attribute *attr, 3 char *buf) 4 { 5 return pm_show_wakelocks(buf, true); 6 } 7 8 static ssize_t wake_lock_store(struct kobject *kobj, 9 struct kobj_attribute *attr, 10 const char *buf, size_t n) 11 { 12 int error = pm_wake_lock(buf); 13 return error ? error : n; 14 } 15 16 power_attr(wake_lock); 17 18 static ssize_t wake_unlock_show(struct kobject *kobj, 19 struct kobj_attribute *attr, 20 char *buf) 21 { 22 return pm_show_wakelocks(buf, false); 23 } 24 25 static ssize_t wake_unlock_store(struct kobject *kobj, 26 struct kobj_attribute *attr, 27 const char *buf, size_t n) 28 { 29 int error = pm_wake_unlock(buf); 30 return error ? error : n; 31 } 32 33 power_attr(wake_unlock); View Code

其 中pm_show_wakelocks 表示

1 ssize_t pm_show_wakelocks(char *buf, bool show_active) 2 { 3 struct rb_node *node; 4 struct wakelock *wl; 5 char *str = buf; 6 char *end = buf + PAGE_SIZE; 7 8 mutex_lock(&wakelocks_lock); 9 10 for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) { //遍历红黑树 11 wl = rb_entry(node, struct wakelock, node); 12 if (wl->ws.active == show_active)// 找满足show_active状态 13 str += scnprintf(str, end - str, "%s ", wl->name); // 把对应的wakeup_lock的名字 14 } 15 if (str > buf) 16 str--; 17 18 str += scnprintf(str, end - str, "\n"); 19 20 mutex_unlock(&wakelocks_lock); 21 return (str - buf); 22 } View Code

关于 pm_wake_lock 表示

1 int pm_wake_lock(const char *buf) 2 { 3 const char *str = buf; 4 struct wakelock *wl; 5 u64 timeout_ns = 0; 6 size_t len; 7 int ret = 0; 8 9 if (!capable(CAP_BLOCK_SUSPEND)) //判断当前进程是否有权限 10 return -EPERM; 11 12 while (*str && !isspace(*str)) 13 str++; 14 15 len = str - buf; 16 if (!len) 17 return -EINVAL; 18 19 if (*str && *str != '\n') { 20 /* Find out if there's a valid timeout string appended. */ 21 ret = kstrtou64(skip_spaces(str), 10, &timeout_ns); 22 if (ret) 23 return -EINVAL; 24 } 25 26 mutex_lock(&wakelocks_lock); 27 28 wl = wakelock_lookup_add(buf, len, true); // 查找是否有相同名字的wakeuplock 29 // 主要根据传进来的buf里面的name和红黑树里面每个node里面的名字进行比较,有则返回对应的指针 30 // 没有则分配空间,并且把传进来的buf里面的wakeuplock信息加入到红黑树里面 31 if (IS_ERR(wl)) { 32 ret = PTR_ERR(wl); 33 goto out; 34 } 35 if (timeout_ns) { // 如果定义了timeout,通过修改定时器,上报一个具有时限的wakeup_event 36 u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1; 37 38 do_div(timeout_ms, NSEC_PER_MSEC); 39 __pm_wakeup_event(&wl->ws, timeout_ms); 40 } else {//否则上报一个没有时限的wakeup_event 41 __pm_stay_awake(&wl->ws); 42 } 43 44 wakelocks_lru_most_recent(wl); 45 46 out: 47 mutex_unlock(&wakelocks_lock); 48 return ret; 49 } View Code

关于pm_wake_unlock表示

1 int pm_wake_unlock(const char *buf) 2 { 3 struct wakelock *wl; 4 size_t len; 5 int ret = 0; 6 7 if (!capable(CAP_BLOCK_SUSPEND)) 8 return -EPERM; 9 10 len = strlen(buf); 11 if (!len) 12 return -EINVAL; 13 14 if (buf[len-1] == '\n') 15 len--; 16 17 if (!len) 18 return -EINVAL; 19 20 mutex_lock(&wakelocks_lock); 21 22 wl = wakelock_lookup_add(buf, len, false); //查找红黑树里面有没有符合条件的wakeuplock 23 if (IS_ERR(wl)) { 24 ret = PTR_ERR(wl); 25 goto out; 26 } 27 __pm_relax(&wl->ws);//deactive对应的wakesource 28 29 wakelocks_lru_most_recent(wl); 30 wakelocks_gc(); 31 32 out: 33 mutex_unlock(&wakelocks_lock); 34 return ret; 35 } View Code

wakelock的垃圾回收机制

主要考虑到wakeup events 建立,销毁,建立的过程太频繁,效率就会降低,所以引入了wakeuplock的垃圾回收机制

主要的原理是:

先保留一些非active状态的wakelocks,等保留的wakelock的数量到达某一个定义的最大值时,则从尾部开始,依次取出wakelock,判断idle的时间,进行注销和释放memory资源

 

Auto Sleep

概念:

当系统没有了正在处理和新增的wakeup events时,就尝试suspend

总体的框架为:

1. sysfs关于autosleep的接口  /sys/power/autosleep

这个sysfs文件的读取 函数 autosleep_show:

1 #ifdef CONFIG_PM_AUTOSLEEP 2 static ssize_t autosleep_show(struct kobject *kobj, 3 struct kobj_attribute *attr, 4 char *buf) 5 { 6 suspend_state_t state = pm_autosleep_state(); //获取当前系统 state,主要包括“freeze”,“standby”,“mem”,“disk”, “off”,“error”等6个字符串 7 8 if (state == PM_SUSPEND_ON) 9 return sprintf(buf, "off\n"); 10 11 #ifdef CONFIG_SUSPEND 12 if (state = PM_SUSPEND_MAX) 6 return -EINVAL; 7 #endif 8 9 __pm_stay_awake(autosleep_ws); // active这个系统,不允许进入suspend 10 11 mutex_lock(&autosleep_lock); 12 13 autosleep_state = state; // 更新系统当前状态 14 15 __pm_relax(autosleep_ws); //运行系统进入休眠 16 17 if (state > PM_SUSPEND_ON) { 18 pm_wakep_autosleep_enabled(true); // autosleep enable 19 queue_up_suspend_work(); //将suspend work挂到 autosleep工作队列里面 20 } else { 21 pm_wakep_autosleep_enabled(false); 22 } 23 24 mutex_unlock(&autosleep_lock); 25 return 0; 26 } View Code

与之有关的函数pm_wakep_autosleep_enabled

1 void pm_wakep_autosleep_enabled(bool set) 2 { 3 struct wakeup_source *ws; 4 ktime_t now = ktime_get(); 5 6 rcu_read_lock(); 7 list_for_each_entry_rcu(ws, &wakeup_sources, entry) { 8 spin_lock_irq(&ws->lock); 9 if (ws->autosleep_enabled != set) { 10 ws->autosleep_enabled = set;//更新和autosleep相关的所有状态 11 if (ws->active) { 12 if (set) 13 ws->start_prevent_time = now; //设置为当前实现,马上阻止进入autosleep 14 else 15 update_prevent_sleep_time(ws, now); 16 } 17 } 18 spin_unlock_irq(&ws->lock); 19 } 20 rcu_read_unlock(); 21 } View Code

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3